{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于时间上下文信息的推荐"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入包\n",
    "import random\n",
    "import math\n",
    "import time\n",
    "import codecs\n",
    "from tqdm import tqdm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一. 通用函数定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 数据处理相关\n",
    "采用Delicious数据集，每次只load一个网站的操作记录\n",
    "1. load data\n",
    "2. split data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dataset():\n",
    "    \n",
    "    # 对每个用户按照时间进行从前到后的排序，取最后一个时间的item作为要预测的测试集\n",
    "    \n",
    "    def __init__(self, site=None):\n",
    "        # site: which site to load\n",
    "        self.bookmark_path = '../dataset/delicious-2k/bookmarks.dat'\n",
    "        self.user_bookmark_path = '../dataset/delicious-2k/user_taggedbookmarks-timestamps.dat'\n",
    "        self.site = site\n",
    "        self.loadData()\n",
    "    \n",
    "    def loadData(self):\n",
    "        bookmarks = [f.strip() for f in codecs.open(self.bookmark_path, 'r', encoding=\"ISO-8859-1\").readlines()][1:]\n",
    "        site_ids = {}\n",
    "        for b in bookmarks:\n",
    "            b = b.split('\\t')\n",
    "            if b[-1] not in site_ids:\n",
    "                site_ids[b[-1]] = set()\n",
    "            site_ids[b[-1]].add(b[0])\n",
    "            \n",
    "        user_bookmarks = [f.strip() for f in codecs.open(self.user_bookmark_path, 'r', encoding=\"ISO-8859-1\").readlines()][1:]\n",
    "        data = {}\n",
    "        cnt = 0\n",
    "        for ub in user_bookmarks:\n",
    "            ub = ub.split('\\t')\n",
    "            if site is None or (site in site_ids and ub[1] in site_ids[site]):\n",
    "                if ub[0] not in data:\n",
    "                    data[ub[0]] = set()\n",
    "                data[ub[0]].add((ub[1], int(ub[3][:-3])))\n",
    "                cnt += 1\n",
    "        self.data = {k: list(sorted(list(data[k]), key=lambda x: x[1], reverse=True)) for k in data}\n",
    "    \n",
    "    def splitData(self):\n",
    "        '''\n",
    "        :params: data, 加载的所有(user, item)数据条目\n",
    "        :return: train, test\n",
    "        '''\n",
    "        train, test = {}, {}\n",
    "        for user in self.data:\n",
    "            if user not in train:\n",
    "                train[user] = []\n",
    "                test[user] = []\n",
    "            data = self.data[user]\n",
    "            train[user].extend(data[1:])\n",
    "            test[user].append(data[0])\n",
    "\n",
    "        return train, test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 评价指标\n",
    "1. Precision\n",
    "2. Recall"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Metric():\n",
    "    \n",
    "    def __init__(self, train, test, GetRecommendation):\n",
    "        '''\n",
    "        :params: train, 训练数据\n",
    "        :params: test, 测试数据\n",
    "        :params: GetRecommendation, 为某个用户获取推荐物品的接口函数\n",
    "        '''\n",
    "        self.train = train\n",
    "        self.test = test\n",
    "        self.GetRecommendation = GetRecommendation\n",
    "        self.recs = self.getRec()\n",
    "        \n",
    "    # 为test中的每个用户进行推荐\n",
    "    def getRec(self):\n",
    "        recs = {}\n",
    "        for user in self.test:\n",
    "            rank = self.GetRecommendation(user)\n",
    "            recs[user] = rank\n",
    "        return recs\n",
    "        \n",
    "    # 定义精确率指标计算方式\n",
    "    def precision(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set([x[0] for x in self.test[user]])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(rank)\n",
    "        return round(hit / all * 100, 2) if all > 0 else 0.0\n",
    "    \n",
    "    # 定义召回率指标计算方式\n",
    "    def recall(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set([x[0] for x in self.test[user]])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(test_items)\n",
    "        return round(hit / all * 100, 2) if all > 0 else 0.0\n",
    "    \n",
    "    def eval(self):\n",
    "        metric = {'Precision': self.precision(),\n",
    "                  'Recall': self.recall()}\n",
    "        return metric"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二. 算法实现\n",
    "1. RecentPopular\n",
    "2. TItemCF\n",
    "3. TUserCF\n",
    "4. ItemCF\n",
    "5. UserCF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. 给用户推荐近期最热门的物品\n",
    "def RecentPopular(train, K, N, alpha=1.0, t0=int(time.time())):\n",
    "    '''\n",
    "    :params: train, 训练数据集\n",
    "    :params: K, 可忽略\n",
    "    :params: N, 超参数，设置取TopN推荐物品数目\n",
    "    :params: alpha, 时间衰减因子\n",
    "    :params: t0, 当前的时间戳\n",
    "    :return: GetRecommendation，推荐接口函数\n",
    "    '''\n",
    "    \n",
    "    item_score = {}\n",
    "    for user in train:\n",
    "        for item, t in train[user]:\n",
    "            if item not in item_score:\n",
    "                item_score[item] = 0\n",
    "            item_score[item] += 1.0 / (alpha * (t0 - t))\n",
    "        \n",
    "    item_score = list(sorted(item_score.items(), key=lambda x: x[1], reverse=True))\n",
    "    \n",
    "    def GetRecommendation(user):\n",
    "        # 随机推荐N个未见过的\n",
    "        user_items = set(train[user])\n",
    "        rec_items = [x for x in item_score if x[0] not in user_items]\n",
    "        return rec_items[:N]\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2. 时间上下文相关的ItemCF算法\n",
    "def TItemCF(train, K, N, alpha=1.0, beta=1.0, t0=int(time.time())):\n",
    "    '''\n",
    "    :params: train, 训练数据集\n",
    "    :params: K, 超参数，设置取TopK相似物品数目\n",
    "    :params: N, 超参数，设置取TopN推荐物品数目\n",
    "    :params: alpha, 计算item相似度的时间衰减因子\n",
    "    :params: beta, 推荐打分时的时间衰减因子\n",
    "    :params: t0, 当前的时间戳\n",
    "    :return: GetRecommendation, 推荐接口函数\n",
    "    '''\n",
    "    # 计算物品相似度矩阵\n",
    "    sim = {}\n",
    "    num = {}\n",
    "    for user in train:\n",
    "        items = train[user]\n",
    "        for i in range(len(items)):\n",
    "            u, t1 = items[i]\n",
    "            if u not in num:\n",
    "                num[u] = 0\n",
    "            num[u] += 1\n",
    "            if u not in sim:\n",
    "                sim[u] = {}\n",
    "            for j in range(len(items)):\n",
    "                if j == i: continue\n",
    "                v, t2 = items[j]\n",
    "                if v not in sim[u]:\n",
    "                    sim[u][v] = 0\n",
    "                sim[u][v] += 1.0 / (alpha * (abs(t1 - t2) + 1))\n",
    "    for u in sim:\n",
    "        for v in sim[u]:\n",
    "            sim[u][v] /= math.sqrt(num[u] * num[v])\n",
    "    \n",
    "    # 按照相似度排序\n",
    "    sorted_item_sim = {k: list(sorted(v.items(), \\\n",
    "                               key=lambda x: x[1], reverse=True)) \\\n",
    "                       for k, v in sim.items()}\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        items = {}\n",
    "        seen_items = set(train[user])\n",
    "        for item, t in train[user]:\n",
    "            for u, _ in sorted_item_sim[item][:K]:\n",
    "                if u not in seen_items:\n",
    "                    if u not in items:\n",
    "                        items[u] = 0\n",
    "                    items[u] += sim[item][u] / (1 + beta * (t0 - t))\n",
    "        recs = list(sorted(items.items(), key=lambda x: x[1], reverse=True))[:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. 时间上下文相关的UserCF算法\n",
    "def TUserCF(train, K, N, alpha=1.0, beta=1.0, t0=int(time.time())):\n",
    "    '''\n",
    "    :params: train, 训练数据集\n",
    "    :params: K, 超参数，设置取TopK相似用户数目\n",
    "    :params: N, 超参数，设置取TopN推荐物品数目\n",
    "    :params: alpha, 计算item相似度的时间衰减因子\n",
    "    :params: beta, 推荐打分时的时间衰减因子\n",
    "    :params: t0, 当前的时间戳\n",
    "    :return: GetRecommendation, 推荐接口函数\n",
    "    '''\n",
    "    # 计算item->user的倒排索引\n",
    "    item_users = {}\n",
    "    for user in train:\n",
    "        for item, t in train[user]:\n",
    "            if item not in item_users:\n",
    "                item_users[item] = []\n",
    "            item_users[item].append((user, t))\n",
    "    \n",
    "    # 计算用户相似度矩阵\n",
    "    sim = {}\n",
    "    num = {}\n",
    "    for item in item_users:\n",
    "        users = item_users[item]\n",
    "        for i in range(len(users)):\n",
    "            u, t1 = users[i]\n",
    "            if u not in num:\n",
    "                num[u] = 0\n",
    "            num[u] += 1\n",
    "            if u not in sim:\n",
    "                sim[u] = {}\n",
    "            for j in range(len(users)):\n",
    "                if j == i: continue\n",
    "                v, t2 = users[j]\n",
    "                if v not in sim[u]:\n",
    "                    sim[u][v] = 0\n",
    "                sim[u][v] += 1.0 / (alpha * (abs(t1 - t2) + 1))\n",
    "    for u in sim:\n",
    "        for v in sim[u]:\n",
    "            sim[u][v] /= math.sqrt(num[u] * num[v])\n",
    "    \n",
    "    # 按照相似度排序\n",
    "    sorted_user_sim = {k: list(sorted(v.items(), \\\n",
    "                               key=lambda x: x[1], reverse=True)) \\\n",
    "                       for k, v in sim.items()}\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        items = {}\n",
    "        seen_items = set(train[user])\n",
    "        recs = []\n",
    "        if user in sorted_user_sim:\n",
    "            for u, _ in sorted_user_sim[user][:K]:\n",
    "                for item, _ in train[u]:\n",
    "                    if item not in seen_items:\n",
    "                        if item not in items:\n",
    "                            items[item] = 0\n",
    "                        items[item] += sim[user][u] / (1 + beta * (t0 - t))\n",
    "            recs = list(sorted(items.items(), key=lambda x: x[1], reverse=True))[:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 4. ItemCF算法\n",
    "def ItemCF(train, K, N):\n",
    "    '''\n",
    "    :params: train, 训练数据集\n",
    "    :params: K, 超参数，设置取TopK相似物品数目\n",
    "    :params: N, 超参数，设置取TopN推荐物品数目\n",
    "    :return: GetRecommendation, 推荐接口函数\n",
    "    '''\n",
    "    # 计算物品相似度矩阵\n",
    "    sim = {}\n",
    "    num = {}\n",
    "    for user in train:\n",
    "        items = train[user]\n",
    "        for i in range(len(items)):\n",
    "            u, _ = items[i]\n",
    "            if u not in num:\n",
    "                num[u] = 0\n",
    "            num[u] += 1\n",
    "            if u not in sim:\n",
    "                sim[u] = {}\n",
    "            for j in range(len(items)):\n",
    "                if j == i: continue\n",
    "                v, _ = items[j]\n",
    "                if v not in sim[u]:\n",
    "                    sim[u][v] = 0\n",
    "                sim[u][v] += 1\n",
    "    for u in sim:\n",
    "        for v in sim[u]:\n",
    "            sim[u][v] /= math.sqrt(num[u] * num[v])\n",
    "    \n",
    "    # 按照相似度排序\n",
    "    sorted_item_sim = {k: list(sorted(v.items(), \\\n",
    "                               key=lambda x: x[1], reverse=True)) \\\n",
    "                       for k, v in sim.items()}\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        items = {}\n",
    "        seen_items = set(train[user])\n",
    "        for item, _ in train[user]:\n",
    "            for u, _ in sorted_item_sim[item][:K]:\n",
    "                if u not in seen_items:\n",
    "                    if u not in items:\n",
    "                        items[u] = 0\n",
    "                    items[u] += sim[item][u]\n",
    "        recs = list(sorted(items.items(), key=lambda x: x[1], reverse=True))[:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5. UserCF算法\n",
    "def UserCF(train, K, N):\n",
    "    '''\n",
    "    :params: train, 训练数据集\n",
    "    :params: K, 超参数，设置取TopK相似用户数目\n",
    "    :params: N, 超参数，设置取TopN推荐物品数目\n",
    "    :return: GetRecommendation, 推荐接口函数\n",
    "    '''\n",
    "    # 计算item->user的倒排索引\n",
    "    item_users = {}\n",
    "    for user in train:\n",
    "        for item, _ in train[user]:\n",
    "            if item not in item_users:\n",
    "                item_users[item] = []\n",
    "            item_users[item].append(user)\n",
    "    \n",
    "    # 计算用户相似度矩阵\n",
    "    sim = {}\n",
    "    num = {}\n",
    "    for item in item_users:\n",
    "        users = item_users[item]\n",
    "        for i in range(len(users)):\n",
    "            u = users[i]\n",
    "            if u not in num:\n",
    "                num[u] = 0\n",
    "            num[u] += 1\n",
    "            if u not in sim:\n",
    "                sim[u] = {}\n",
    "            for j in range(len(users)):\n",
    "                if j == i: continue\n",
    "                v = users[j]\n",
    "                if v not in sim[u]:\n",
    "                    sim[u][v] = 0\n",
    "                sim[u][v] += 1\n",
    "    for u in sim:\n",
    "        for v in sim[u]:\n",
    "            sim[u][v] /= math.sqrt(num[u] * num[v])\n",
    "    \n",
    "    # 按照相似度排序\n",
    "    sorted_user_sim = {k: list(sorted(v.items(), \\\n",
    "                               key=lambda x: x[1], reverse=True)) \\\n",
    "                       for k, v in sim.items()}\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        items = {}\n",
    "        seen_items = set(train[user])\n",
    "        recs = []\n",
    "        if user in sorted_user_sim:\n",
    "            for u, _ in sorted_user_sim[user][:K]:\n",
    "                for item, _ in train[u]:\n",
    "                    # 要去掉用户见过的\n",
    "                    if item not in seen_items:\n",
    "                        if item not in items:\n",
    "                            items[item] = 0\n",
    "                        items[item] += sim[user][u]\n",
    "            recs = list(sorted(items.items(), key=lambda x: x[1], reverse=True))[:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 三. 实验\n",
    "1. RecentPopular实验\n",
    "2. TItemCF实验\n",
    "3. TUserCF实验\n",
    "4. ItemCF实验\n",
    "5. UserCF实验\n",
    "\n",
    "K=10，N=[10, 20, 30, ..., 100]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Experiment():\n",
    "    \n",
    "    def __init__(self, K, N, site=None, rt='RecentPopular'):\n",
    "        '''\n",
    "        :params: K, TopK相似的个数\n",
    "        :params: N, TopN推荐物品的个数\n",
    "        :params: site, 选择一个网站的记录进行推荐\n",
    "        :params: rt, 推荐算法类型\n",
    "        '''\n",
    "        self.K = K\n",
    "        self.N = N\n",
    "        self.site = site\n",
    "        self.rt = rt\n",
    "        self.alg = {'RecentPopular': RecentPopular, 'TItemCF': TItemCF, \\\n",
    "                    'TUserCF': TUserCF, 'ItemCF': ItemCF, 'UserCF': UserCF}\n",
    "    \n",
    "    # 定义单次实验\n",
    "    def worker(self, train, test):\n",
    "        '''\n",
    "        :params: train, 训练数据集\n",
    "        :params: test, 测试数据集\n",
    "        :return: 各指标的值\n",
    "        '''\n",
    "        getRecommendation = self.alg[self.rt](train, self.K, self.N)\n",
    "        metric = Metric(train, test, getRecommendation)\n",
    "        return metric.eval()\n",
    "    \n",
    "    # 运行实验\n",
    "    def run(self):\n",
    "        dataset = Dataset(self.site)\n",
    "        train, test = dataset.splitData()\n",
    "        metric = self.worker(train, test)\n",
    "        print('Result (site={}, K={}, N={}): {}'.format(\\\n",
    "                       self.site, self.K, self.N, metric))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result (site=www.nytimes.com, K=0, N=10): {'Precision': 0.16, 'Recall': 1.58}\n",
      "Result (site=www.nytimes.com, K=0, N=20): {'Precision': 0.11, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=0, N=30): {'Precision': 0.09, 'Recall': 2.71}\n",
      "Result (site=www.nytimes.com, K=0, N=40): {'Precision': 0.08, 'Recall': 3.39}\n",
      "Result (site=www.nytimes.com, K=0, N=50): {'Precision': 0.08, 'Recall': 3.84}\n",
      "Result (site=www.nytimes.com, K=0, N=60): {'Precision': 0.1, 'Recall': 6.09}\n",
      "Result (site=www.nytimes.com, K=0, N=70): {'Precision': 0.09, 'Recall': 6.09}\n",
      "Result (site=www.nytimes.com, K=0, N=80): {'Precision': 0.08, 'Recall': 6.55}\n",
      "Result (site=www.nytimes.com, K=0, N=90): {'Precision': 0.09, 'Recall': 7.67}\n",
      "Result (site=www.nytimes.com, K=0, N=100): {'Precision': 0.09, 'Recall': 8.58}\n",
      "Result (site=en.wikipedia.org, K=0, N=10): {'Precision': 0.0, 'Recall': 0.0}\n",
      "Result (site=en.wikipedia.org, K=0, N=20): {'Precision': 0.01, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=0, N=30): {'Precision': 0.02, 'Recall': 0.49}\n",
      "Result (site=en.wikipedia.org, K=0, N=40): {'Precision': 0.01, 'Recall': 0.49}\n",
      "Result (site=en.wikipedia.org, K=0, N=50): {'Precision': 0.01, 'Recall': 0.49}\n",
      "Result (site=en.wikipedia.org, K=0, N=60): {'Precision': 0.01, 'Recall': 0.49}\n",
      "Result (site=en.wikipedia.org, K=0, N=70): {'Precision': 0.01, 'Recall': 0.99}\n",
      "Result (site=en.wikipedia.org, K=0, N=80): {'Precision': 0.02, 'Recall': 1.23}\n",
      "Result (site=en.wikipedia.org, K=0, N=90): {'Precision': 0.01, 'Recall': 1.23}\n",
      "Result (site=en.wikipedia.org, K=0, N=100): {'Precision': 0.01, 'Recall': 1.48}\n"
     ]
    }
   ],
   "source": [
    "# 1. RecentPopular实验\n",
    "K = 0 # 为保持一致而设置，随便填一个值\n",
    "for site in ['www.nytimes.com', 'en.wikipedia.org']:\n",
    "    for N in range(10, 110, 10):\n",
    "        exp = Experiment(K, N, site=site, rt='RecentPopular')\n",
    "        exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 2.26, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.14, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.13, 'Recall': 2.48}\n",
      "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.36, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.35, 'Recall': 0.25}\n"
     ]
    }
   ],
   "source": [
    "# 2. TItemCF实验\n",
    "K = 10\n",
    "for site in ['www.nytimes.com', 'en.wikipedia.org']:\n",
    "    for N in range(10, 110, 10):\n",
    "        exp = Experiment(K, N, site=site, rt='TItemCF')\n",
    "        exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 3.36, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.6, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.72, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.87, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.86, 'Recall': 0.25}\n"
     ]
    }
   ],
   "source": [
    "# 3. TUserCF实验\n",
    "K = 10\n",
    "for site in ['www.nytimes.com', 'en.wikipedia.org']:\n",
    "    for N in range(10, 110, 10):\n",
    "        exp = Experiment(K, N, site=site, rt='TUserCF')\n",
    "        exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 2.26, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 1.99, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 1.96, 'Recall': 2.26}\n",
      "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.36, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.35, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.35, 'Recall': 0.25}\n"
     ]
    }
   ],
   "source": [
    "# 4. ItemCF实验\n",
    "K = 10\n",
    "for site in ['www.nytimes.com', 'en.wikipedia.org']:\n",
    "    for N in range(10, 110, 10):\n",
    "        exp = Experiment(K, N, site=site, rt='ItemCF')\n",
    "        exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 3.69, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.86, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.72, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.71, 'Recall': 2.48}\n",
      "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.87, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.86, 'Recall': 0.25}\n",
      "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.86, 'Recall': 0.25}\n"
     ]
    }
   ],
   "source": [
    "# 5. UserCF实验\n",
    "K = 10\n",
    "for site in ['www.nytimes.com', 'en.wikipedia.org']:\n",
    "    for N in range(10, 110, 10):\n",
    "        exp = Experiment(K, N, site=site, rt='UserCF')\n",
    "        exp.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 四. 实验结果（请双击查看）\n",
    "\n",
    "1. RecentPopular实验\n",
    "Result (site=www.nytimes.com, K=0, N=10): {'Precision': 0.16, 'Recall': 1.58}\n",
    "Result (site=www.nytimes.com, K=0, N=20): {'Precision': 0.11, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=0, N=30): {'Precision': 0.09, 'Recall': 2.71}\n",
    "Result (site=www.nytimes.com, K=0, N=40): {'Precision': 0.08, 'Recall': 3.39}\n",
    "Result (site=www.nytimes.com, K=0, N=50): {'Precision': 0.08, 'Recall': 3.84}\n",
    "Result (site=www.nytimes.com, K=0, N=60): {'Precision': 0.1, 'Recall': 6.09}\n",
    "Result (site=www.nytimes.com, K=0, N=70): {'Precision': 0.09, 'Recall': 6.09}\n",
    "Result (site=www.nytimes.com, K=0, N=80): {'Precision': 0.08, 'Recall': 6.55}\n",
    "Result (site=www.nytimes.com, K=0, N=90): {'Precision': 0.09, 'Recall': 7.67}\n",
    "Result (site=www.nytimes.com, K=0, N=100): {'Precision': 0.09, 'Recall': 8.58}\n",
    "Result (site=en.wikipedia.org, K=0, N=10): {'Precision': 0.0, 'Recall': 0.0}\n",
    "Result (site=en.wikipedia.org, K=0, N=20): {'Precision': 0.01, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=0, N=30): {'Precision': 0.02, 'Recall': 0.49}\n",
    "Result (site=en.wikipedia.org, K=0, N=40): {'Precision': 0.01, 'Recall': 0.49}\n",
    "Result (site=en.wikipedia.org, K=0, N=50): {'Precision': 0.01, 'Recall': 0.49}\n",
    "Result (site=en.wikipedia.org, K=0, N=60): {'Precision': 0.01, 'Recall': 0.49}\n",
    "Result (site=en.wikipedia.org, K=0, N=70): {'Precision': 0.01, 'Recall': 0.99}\n",
    "Result (site=en.wikipedia.org, K=0, N=80): {'Precision': 0.02, 'Recall': 1.23}\n",
    "Result (site=en.wikipedia.org, K=0, N=90): {'Precision': 0.01, 'Recall': 1.23}\n",
    "Result (site=en.wikipedia.org, K=0, N=100): {'Precision': 0.01, 'Recall': 1.48}\n",
    "     \n",
    "2. TItemCF实验\n",
    "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 2.26, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.14, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.13, 'Recall': 2.48}\n",
    "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.36, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.35, 'Recall': 0.25}\n",
    "   \n",
    "3. TUserCF实验\n",
    "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 3.36, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.6, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.72, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.87, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.86, 'Recall': 0.25}\n",
    "   \n",
    "4. ItemCF实验\n",
    "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 2.26, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 1.99, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 1.96, 'Recall': 2.26}\n",
    "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.36, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.35, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.35, 'Recall': 0.25}\n",
    "    \n",
    "5. UserCF实验\n",
    "Result (site=www.nytimes.com, K=10, N=10): {'Precision': 3.69, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=20): {'Precision': 2.86, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=30): {'Precision': 2.72, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=40): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=50): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=60): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=70): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=80): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=90): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=www.nytimes.com, K=10, N=100): {'Precision': 2.71, 'Recall': 2.48}\n",
    "Result (site=en.wikipedia.org, K=10, N=10): {'Precision': 0.87, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=20): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=30): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=40): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=50): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=60): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=70): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=80): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=90): {'Precision': 0.86, 'Recall': 0.25}\n",
    "Result (site=en.wikipedia.org, K=10, N=100): {'Precision': 0.86, 'Recall': 0.25}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
